package cn.com.nes.site.entity.iot;

import cn.com.nes.common.util.listener.IdUtil;
import cn.com.nes.mybatis.agent.ope.entity.IsDataItemField;
import cn.com.nes.mybatis.agent.ope.entity.IsDeviceProtocolDataItemInfo;
import cn.com.nes.mybatis.agent.ope.entity.IsEquipmentFunctionDetailInfo;
import cn.com.nes.mybatis.agent.ope.entity.IsProtocolControlCommand;
import cn.com.nes.site.entity.em.DataType;
import cn.com.nes.site.entity.em.RegisterTypeEnum;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.excel.annotation.ExcelIgnore;
import com.alibaba.excel.annotation.ExcelProperty;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

public class RegisterDataItem {
    @ExcelProperty("数据项ID")
    private String dataItemId;
    @ExcelIgnore
    private String manufactureId;
    @ExcelProperty("协议ID")
    private String protocolId;
    // 寄存器地址
    @ExcelProperty("寄存器地址")
    private Integer registerAddress;
    // 位地址，单个数字或数字范围（如：0-3）
//    private Integer bitAddress;
    private String bitAddress;

    // 数据名称
    @ExcelProperty("数据项名称")
    private String dataName;
    // 数据单位
    @ExcelProperty("数据项单位")
    private String dataUnit;
    // 寄存器类型
    @ExcelProperty("寄存器类型")
    private String registerType;
    // 可写
    @ExcelProperty("可写")
    private Boolean writable;
    @ExcelProperty("可读")
    private Boolean readable = true;
    // 数据类型
    @ExcelProperty("数据类型")
    private String dataType;
    // 故障检测
    private Boolean faultDetection;
    // 故障正常值
    private String faultNormalValue;
    // 上限
    private BigDecimal upperLimit;
    // 下限
    private BigDecimal lowerLimit;
    // 公式
    private String formula;
    // 控制公式
    private String controlFormula;
    // 数据枚举
    private String dataEnum;
    // 控制参数枚举
    private String controlParamEnum;

    private String classify;

    private Integer commandCut;

    private BigDecimal showRangeUp;

    private BigDecimal showRangeLow;

    private String errorValue;

    private String errorReplaceValue;

    public String getErrorValue() {
        return errorValue;
    }

    public void setErrorValue(String errorValue) {
        this.errorValue = errorValue;
    }

    public String getErrorReplaceValue() {
        return errorReplaceValue;
    }

    public void setErrorReplaceValue(String errorReplaceValue) {
        this.errorReplaceValue = errorReplaceValue;
    }

    public BigDecimal getShowRangeUp() {
        return showRangeUp;
    }

    public void setShowRangeUp(BigDecimal showRangeUp) {
        this.showRangeUp = showRangeUp;
    }

    public BigDecimal getShowRangeLow() {
        return showRangeLow;
    }

    public void setShowRangeLow(BigDecimal showRangeLow) {
        this.showRangeLow = showRangeLow;
    }

    public Integer getCommandCut() {
        return commandCut;
    }

    public void setCommandCut(Integer commandCut) {
        this.commandCut = commandCut;
    }


    private Integer bitPlace;
    private Integer bitPlaceEnd;

    public Integer getBitPlace() {
        return bitPlace;
    }

    public void setBitPlace(Integer bitPlace) {
        this.bitPlace = bitPlace;
    }

    public Integer getBitPlaceEnd() {
        return bitPlaceEnd;
    }

    public void setBitPlaceEnd(Integer bitPlaceEnd) {
        this.bitPlaceEnd = bitPlaceEnd;
    }

    public RegisterDataItem(IsDeviceProtocolDataItemInfo dataItemInfo)
    {
        this.dataItemId = dataItemInfo.getDataItemId();
        this.manufactureId = dataItemInfo.getManufactureId();
        this.protocolId = dataItemInfo.getProtocolId();
        this.registerAddress = dataItemInfo.getRegisterAddress();

        // modify by zhouy
        this.setBitPlace(dataItemInfo.getBitPlace());
        this.setBitPlaceEnd(dataItemInfo.getBitPlaceEnd());

        this.dataName = dataItemInfo.getName();
        this.dataUnit = dataItemInfo.getUnitValue();
        this.registerType = dataItemInfo.getRegisterType();
        this.writable = false;
        // add by zhouy
        // -----------------------------------------------------------------------
        // 数据库里没有存可读的值时，设置可读信息
        if (dataItemInfo.getIsReadWrite() == null) {
            this.readable = true;
        }else{
            if(dataItemInfo.getIsReadWrite()==2){
                this.readable = true;
            }else{
                this.readable = false;
            }
        }
        // -----------------------------------------------------------------------
        this.dataType = dataItemInfo.getDataTypeEnum();
        this.faultDetection = dataItemInfo.getIsCheckFault() == 1;
        this.faultNormalValue = dataItemInfo.getNormalValue();
        this.upperLimit = dataItemInfo.getReasonableRangeUp();
        this.lowerLimit = dataItemInfo.getReasonableRangeLow();
        this.formula = dataItemInfo.getFormula();
        this.classify = dataItemInfo.getClassify();
        this.commandCut = dataItemInfo.getCommandCut();
        this.showRangeUp = dataItemInfo.getShowRangeUp();
        this.showRangeLow = dataItemInfo.getShowRangeLow();
        this.errorValue = dataItemInfo.getErrorValue();
        this.errorReplaceValue = dataItemInfo.getErrorReplaceValue();
    }

    public RegisterDataItem() {

    }


    public Integer getRegisterAddress() {
        return registerAddress;
    }

    public void setRegisterAddress(Integer registerAddress) {
        this.registerAddress = registerAddress;
    }

//    public Integer getBitAddress() {
//        return bitAddress;
//    }
//
//    public void setBitAddress(Integer bitAddress) {
//        this.bitAddress = bitAddress;
//    }


    /*
    public String getBitAddress() {
        return bitAddress;
    }*/


    /*
    public Integer getBitAddress(){
        if (ObjUtil.isNotEmpty(bitAddress)) {
            if (bitAddress.contains("-")) {
                return Integer.valueOf(bitAddress.split("-")[0]);
            }else{
                return Integer.valueOf(bitAddress);
            }
        }else{
            return null;
        }

    }

    public Integer getBitAddressEnd(){

        if (ObjUtil.isNotEmpty(bitAddress)) {
        if (bitAddress.contains("-")) {
            return Integer.valueOf(bitAddress.split("-")[1]);
        }else{
            return null;
        }}else{
            return null;
        }
    }
     */

    public void setBitAddress(String bitAddress) {
        if (StrUtil.isNotBlank(bitAddress)) {
            if (bitAddress.contains("-")) {
                String[] strs = bitAddress.split("-");
                setBitPlace(Integer.valueOf(strs[0]));
                setBitPlaceEnd(Integer.valueOf(strs[1]));
            }else{
                setBitPlace(Integer.valueOf(bitAddress));
            }
        }
        this.bitAddress = bitAddress;
    }

    public String getOrgBitAddress(){
        return this.bitAddress;
    }

    public String getBitAddress(){
        String result= null;
        if (bitPlace != null) {
            result = String.valueOf(bitPlace);
        }
        if (bitPlaceEnd != null) {
            result = result + '-' +String.valueOf(bitPlaceEnd);
        }
        return result;
    }

    public String getDataName() {
        return dataName;
    }

    public void setDataName(String dataName) {
        this.dataName = dataName;
    }

    public String getDataUnit() {
        return dataUnit;
    }

    public void setDataUnit(String dataUnit) {
        this.dataUnit = dataUnit;
    }

    public String getRegisterType() {
        return registerType;
    }

    public void setRegisterType(String registerType) {
        this.registerType = registerType;
    }

    public Boolean getWritable() {
        return writable;
    }

    public void setWritable(Boolean writable) {
        this.writable = writable;
    }

    public String getDataType() {
        return dataType;
    }

    public void setDataType(String dataType) {
        this.dataType = dataType;
    }

    public Boolean getFaultDetection() {
        return faultDetection;
    }

    public void setFaultDetection(Boolean faultDetection) {
        this.faultDetection = faultDetection;
    }

    public String getFaultNormalValue() {
        return faultNormalValue;
    }

    public void setFaultNormalValue(String faultNormalValue) {
        this.faultNormalValue = faultNormalValue;
    }

    public BigDecimal getUpperLimit() {
        return upperLimit;
    }

    public void setUpperLimit(BigDecimal upperLimit) {
        this.upperLimit = upperLimit;
    }

    public BigDecimal getLowerLimit() {
        return lowerLimit;
    }

    public void setLowerLimit(BigDecimal lowerLimit) {
        this.lowerLimit = lowerLimit;
    }

    public String getFormula() {
        return formula;
    }

    public void setFormula(String formula) {
        this.formula = formula;
    }

    public String getDataEnum() {
        return dataEnum;
    }

    public void setDataEnum(String dataEnum) {
        this.dataEnum = dataEnum;
    }

    public String getControlParamEnum() {
        return controlParamEnum;
    }

    public void setControlParamEnum(String controlParamEnum) {
        this.controlParamEnum = controlParamEnum;
    }

    public Boolean getReadable() {
        return readable;
    }

    public void setReadable(Boolean readable) {
        this.readable = readable;
    }

    public void setRead(int ntype){
        if(ntype == 3){
            this.readable = false;
        }else{
            this.readable = true;
        }
    }

    public void setReadWrite(int ntype){
        // 此处对应的是excel中的属性，0 可读写，1只读，2只写
        // 如果对应数据库中存的值，2 代表可读，3 代表不可读
        if(ntype==0){
            this.readable = true;
            this.writable = true;
        }else if(ntype ==1){
            this.readable = true;
            this.writable = false;
        }else if(ntype ==2){
            this.readable = false;
            this.writable = true;
        }else{
            this.readable = true;
            this.writable = false;
        }
    }


    public NesProtocolDataSet convertToNesFormat(String userId, String slaveAddress) {
        NesProtocolDataSet nesProtocol = new NesProtocolDataSet();
        nesProtocol.setRegisterDataItem(this);

        // 生成数据项
        final IsDeviceProtocolDataItemInfo nesDataItem = generateDataItemInfo(userId);
        nesProtocol.setDataItemInfo(nesDataItem);
        // 数据项详情
        if (StrUtil.isNotBlank(this.dataEnum)) {
            final List<IsDataItemField> dataItemField = generateDataItemField(userId);
            nesProtocol.setDataItemField(dataItemField);
        }

        // 控制指令
        if (this.writable) {
            final IsProtocolControlCommand controlCommand = generateControlCommand(userId, slaveAddress);
            // add by zhouy(如果不可读,设置控制为不需要查询结果确认)
            if(!this.readable) {
                controlCommand.setIsQueryData(0);
            }
            nesProtocol.setControlCommand(controlCommand);
            // 控制指令详情
            if (StrUtil.isNotBlank(this.dataEnum) && StrUtil.isBlank(this.controlParamEnum)) {
                this.controlParamEnum = this.dataEnum;
            }
            List<IsEquipmentFunctionDetailInfo> controlParam = generateControlParams(controlCommand.getControlCommandId(),nesDataItem.getReasonableRangeUp(),nesDataItem.getReasonableRangeLow());
            nesProtocol.setControlCommandParams(controlParam);
        }

        return nesProtocol;
    }

    public IsDeviceProtocolDataItemInfo generateDataItemInfo(String userId) {
        final IsDeviceProtocolDataItemInfo nesDataItem = new IsDeviceProtocolDataItemInfo();
        // 指令id后续生成
        nesDataItem.setQueryCommandId("");
        // 模板id后续生成
        nesDataItem.setTemplateId("");
        // 起始位置后续生成
        nesDataItem.setStartByte(-1);

        nesDataItem.setDataItemId(this.dataItemId);
        nesDataItem.setManufactureId(this.manufactureId);
        nesDataItem.setProtocolId(this.protocolId);
        nesDataItem.setName(this.dataName);
        nesDataItem.setUnitValue(this.dataUnit);
        nesDataItem.setRegisterAddress(this.registerAddress);
        nesDataItem.setRegisterType(this.registerType);
        final RegisterTypeEnum registerType = RegisterTypeEnum.valueOf(this.registerType);
        // 线圈寄存器、离散输入寄存器，数据类型为二进制
        final DataType dataTypeEnum = registerType.getBitLength() == 1
                ? DataType.BINARY
                : DataType.valueOf(this.dataType);
        Integer length = dataTypeEnum.getLength();
        // 数据类型不定长，按位地址长度计算
//        if (length < 0) length = getBitLength(bitAddress);
        // 默认按寄存器bit长度计算
//        if (length < 0) length = registerType.getBitLength();
        // bit长度
        nesDataItem.setByteLength(dataTypeEnum.getLength());
        nesDataItem.setConvertId(dataTypeEnum.getConvertId());
        nesDataItem.setFormula(this.formula);
        nesDataItem.setIsCheckFault(this.faultDetection ? 1 : 0);
        nesDataItem.setNormalValue(this.faultNormalValue);
        nesDataItem.setValueType(2);
        nesDataItem.setDataType(dataTypeEnum.getUnsigned());
        nesDataItem.setSjly("hot_chain");
        nesDataItem.setEndian(dataTypeEnum.getEndian());

        nesDataItem.setBitPlace(registerType.getBitLength() == 1 ? Integer.valueOf(0) : this.getBitPlace());
        nesDataItem.setBitPlaceEnd(this.getBitPlaceEnd());
//        nesDataItem.setBitPlace(registerType.getBitLength() == 1 ? Integer.valueOf(0) : this.bitAddress);
        if (null != this.upperLimit) nesDataItem.setReasonableRangeUp(this.upperLimit);
        if (null != this.lowerLimit) nesDataItem.setReasonableRangeLow(this.lowerLimit);
        // todo 考虑兼容ascii
        nesDataItem.setFieldType(1);
        final Date now = new Date();
        if (StrUtil.isBlank(this.dataItemId)) {
            nesDataItem.setCrOper(userId);
            nesDataItem.setGmtCreate(now);
        }
        nesDataItem.setUpOper(userId);
        nesDataItem.setGmtModified(now);
        nesDataItem.setVersion(now.getTime());
        nesDataItem.setDataTypeEnum(dataTypeEnum.toString());
        nesDataItem.setClassify(this.classify);
        nesDataItem.setCommandCut(this.getCommandCut());
        nesDataItem.setShowRangeLow(this.getShowRangeLow());
        nesDataItem.setShowRangeUp(this.getShowRangeUp());
        nesDataItem.setErrorValue(this.getErrorValue());
        nesDataItem.setErrorReplaceValue(this.getErrorReplaceValue());

        return nesDataItem;
    }

    public List<IsDataItemField> generateDataItemField(String userId) {
        final List<IsDataItemField> fields = new ArrayList<>();
        Date now = new Date();
        // 替换中文符号
        this.dataEnum = this.dataEnum.replace("；", ";");
        this.dataEnum = this.dataEnum.replace("：", ":");
        String[] rules = dataEnum.split(";");
        for (String rule : rules) {
            String[] ra = new String[2];
            if (parseEnumRule(rule, ra)) {
                continue;
            }
            final String key = ra[0];
            final String value = ra[1];
            IsDataItemField field = new IsDataItemField();
            field.setDataItemFieldId("difi_" + IdUtil.getIncreaseIdByNanoTime());
            field.setDeviceProtocolId(protocolId);
            field.setManufactureId(manufactureId);
            field.setDataItemId(dataItemId);
            field.setName(value);
            field.setValue(key);
            field.setIsDeleted(0);
            field.setGmtCreate(now);
            field.setGmtModified(now);
            field.setCrOper(userId);
            field.setUpOper(userId);
            field.setVersion(now.getTime());
            field.setSjly("hot_chain");
            field.setName(value);
            field.setGmtModified(now);
            field.setUpOper("system");
            field.setVersion(now.getTime());

            fields.add(field);
        }
        return fields;
    }

    private boolean parseEnumRule(String rule, String[] ra) {
        String[] result =  rule.split(":");
        String key = result[0].trim();
        String value =result[1].trim();
        if (!NumberUtil.isInteger(key) || StrUtil.isBlank(value)) {
            return true;
        }
        ra[0] = key;
        ra[1] = value;
        if(result.length>2) {
            ra[2] = result[2];
        }
        return false;
        /*
        rule = rule.trim();
        final int i = rule.indexOf(":");
        if (i < 1 || i == rule.length() - 1) {
            return true;
        }
        String key = rule.substring(0, i).trim();
        String value = rule.substring(i + 1).trim();
        // 校验
        if (!NumberUtil.isInteger(key) || StrUtil.isBlank(value)) {
            return true;
        }
        ra[0] = key;
        ra[1] = value;
        return false;*/

    }

    public IsProtocolControlCommand generateControlCommand(String userId, String slaveAddress) {
        Date now = new Date();
        RegisterTypeEnum registerType = RegisterTypeEnum.valueOf(this.registerType);
        DataType dataTypeEnum = DataType.valueOf(this.dataType);
        final IsProtocolControlCommand controlCommand = new IsProtocolControlCommand();
        controlCommand.setProtocolId(this.protocolId);
        controlCommand.setName(this.dataName + "控制");
        if (registerAddress == null) {
            throw new RuntimeException("寄存器地址不能为空, 数据项名称: " + this.dataName);
        }
        String header = slaveAddress + registerType.getWriteFunctionCode()
                + StrUtil.padPre(Integer.toHexString(this.registerAddress), 4, '0');
        header = header.toUpperCase();
        controlCommand.setCommand(header.toUpperCase() + "{val}");
        controlCommand.setValueLength(dataTypeEnum.getLength() < 16 ? 16 : dataTypeEnum.getLength());
        // 线圈寄存器控制值为0或65280，10进制转16进制
        controlCommand.setControlConvertId(registerType.equals(RegisterTypeEnum.DO)
                ? 0
                : dataTypeEnum.getControlConvertId());
        controlCommand.setResponseHeader(header);
        controlCommand.setResponseLength(header.length() * 4);
        controlCommand.setFormula(this.controlFormula);
        controlCommand.setUpOper(userId);
        controlCommand.setGmtModified(now);
        controlCommand.setVersion(now.getTime());
        controlCommand.setCrOper(userId);
        controlCommand.setGmtCreate(now);

        /*
        controlCommand.setIsQueryData(bitAddress != null && Integer.bitAddress > -1 ? 1 : 0);
        if (bitAddress != null && bitAddress > -1 && registerType.getBitLength() > 1) {
            controlCommand.setControlBitPlace(bitAddress);
            controlCommand.setControlBitPlaceEnd(bitAddress);
        }*/

        // modify by zhouy
        controlCommand.setIsQueryData(bitAddress != null ? 1 : 0);
//        if (bitAddress != null  && registerType.getBitLength() > 1) {
        if (StrUtil.isNotBlank(bitAddress) && registerType.getBitLength() > 1) {
            if (bitAddress.contains("-")) {
                String[] strs = bitAddress.split("-");
                if (strs.length == 2) {
                    controlCommand.setControlBitPlace(Integer.valueOf(strs[0]));
                    controlCommand.setControlBitPlaceEnd(Integer.valueOf(strs[1]));
                }else{
                    throw new RuntimeException("寄存器位地址不正确, 位地址: " + this.bitAddress);
                }
            }else {
                controlCommand.setControlBitPlace(Integer.valueOf(bitAddress));
                controlCommand.setControlBitPlaceEnd(Integer.valueOf(bitAddress));
            }
        }

        controlCommand.setControlCommandId("pcc_" + IdUtil.getIncreaseIdByNanoTime());
        return controlCommand;
    }

    public List<IsEquipmentFunctionDetailInfo> generateControlParams(String controlCommandId,BigDecimal uplimit,BigDecimal lowlimit) {
        final List<IsEquipmentFunctionDetailInfo> controlParams = new ArrayList<>();
        Date now = new Date();
        if (StrUtil.isNotBlank(this.controlParamEnum)) {
            // 替换中文符号
            this.controlParamEnum = this.controlParamEnum.replace("；", ";");
            this.controlParamEnum = this.controlParamEnum.replace("：", ":");
            String[] rules = controlParamEnum.split(";");
            int code = 0;
            for (String rule : rules) {
                String[] ra = new String[3];
                if (parseEnumRule(rule, ra)) {
                    continue;
                }
                final String key = ra[0];
                final String value = ra[1];
                final String relateValue = ra[2];

                IsEquipmentFunctionDetailInfo param = generateCommandParam(controlCommandId, now, code, key, value,uplimit,lowlimit,relateValue);

                controlParams.add(param);
                code++;
            }
        }
        // 数值型的控制命令只生成一个控制参数
        else {
            final IsEquipmentFunctionDetailInfo param = generateCommandParam(controlCommandId, now, 0, null, this.dataName,uplimit,lowlimit,null);
            controlParams.add(param);
        }
        return controlParams;
    }

    private IsEquipmentFunctionDetailInfo generateCommandParam(String controlCommandId, Date now, int code,
                                                               String key, String value,BigDecimal upLimit,BigDecimal lowLimit,String relateValue) {
        IsEquipmentFunctionDetailInfo param = new IsEquipmentFunctionDetailInfo();
        param.setDetailId("efd_" + IdUtil.getIncreaseIdByNanoTime());
        param.setProtocolId(protocolId);
        param.setManufactureId(manufactureId);
        param.setControlCommandId(controlCommandId);
        // 控制码设为功能码+寄存器地址
        RegisterTypeEnum registerTypeEnum = RegisterTypeEnum.valueOf(this.registerType);
        param.setCode(registerTypeEnum.getWriteFunctionCode() +
                StrUtil.padPre(Integer.toHexString(this.registerAddress), 4, '0').toUpperCase());
        final String subCode = StrUtil.padPre(String.valueOf(code + 1), 2, '0');
        String functionCode = param.getCode() + subCode;
        param.setFunctionCode(functionCode);
        param.setName(value);
        // 线圈寄存器控制值为0或65280
        if (key != null) {
            param.setValue(RegisterTypeEnum.DO.toString().equals(registerType) && "1".equals(key)
                    ? new BigDecimal(65280)
                    : new BigDecimal(key));
        }

        param.setLowLimit(lowLimit);
        param.setUpLimit(upLimit);
        param.setSfsc(0);
        param.setCjsj(now);
        param.setXgsj(now);
        param.setSjly("hot_chain");
        param.setName(value);
        param.setRelateValue(relateValue);

        return param;
    }

    public static void main(String[] args) {
        final BigDecimal bigDecimal = new BigDecimal("0 ");
        System.out.println(bigDecimal);
    }

    private Integer getBitPlace(String bitAddress) {
        if (StrUtil.isBlank(bitAddress)) {
            return null;
        } else if (bitAddress.contains("-")) {
            return Integer.parseInt(bitAddress.split("-")[0]);
        }
        return Integer.parseInt(bitAddress);
    }

    // 正则校验bit address
    private boolean checkBitAddress(String bitAddress) {
        if (StrUtil.isBlank(bitAddress)) {
            return true;
        }
        return bitAddress.matches("^(\\d+-\\d+)|(\\d+)$")
                && (bitAddress.contains("-")
                && Integer.parseInt(bitAddress.split("-")[0]) < Integer.parseInt(bitAddress.split("-")[1]));
    }

    /**
     * 根据bit地址计算bit长度
     *
     * @param bitAddress
     * @return
     */
    private Integer getBitLength(String bitAddress) {
        if (StrUtil.isBlank(bitAddress)) {
            return -1;
        }
        if (bitAddress.contains("-")) {
            final String[] bitAddressArr = bitAddress.split("-");
            return Integer.parseInt(bitAddressArr[1]) - Integer.parseInt(bitAddressArr[0]) + 1;
        }
        return 1;
    }

    public String getDataItemId() {
        return dataItemId;
    }

    public void setDataItemId(String dataItemId) {
        this.dataItemId = dataItemId;
    }

    public String getManufactureId() {
        return manufactureId;
    }

    public void setManufactureId(String manufactureId) {
        this.manufactureId = manufactureId;
    }

    public String getProtocolId() {
        return protocolId;
    }

    public void setProtocolId(String protocolId) {
        this.protocolId = protocolId;
    }

    public String getControlFormula() {
        return controlFormula;
    }

    public void setControlFormula(String controlFormula) {
        this.controlFormula = controlFormula;
    }

    public String getClassify() {
        return classify;
    }

    public void setClassify(String classify) {
        this.classify = classify;
    }
}
